import java.util.ArrayList;
import java.util.Iterator;

/**
 * Tomasulo Scheduler
 * @author KM
 *
 */
public class Scheduler {
	private ArrayList <String> instructions;
	private ArrayList <Instruction> inst;
	private ArrayList <Instruction> inst2;
	ArrayList <Register> regFile;
	private int clock;
	private int m;
	//Fetch rate
	private int N;
	//Dispatch rate
	private int D;
	//size of function units
	private int k0, k1, k2;
	private Fetch fetch;
	private DispatchQueue dispQ;
	private SchedulingQueue schedQ;
	private int index = 0;
	/** 
	 * Constructor for Scheduler
	 * @param lines list of instructions
	 */
	public Scheduler(ArrayList<String> lines, int N, int m, int D, int k0, int k1, int k2) {
		instructions = lines;
		inst = new ArrayList();
		inst2 = new ArrayList();
		regFile = new ArrayList();
		for (int i = 0; i < 128; i++) {
			regFile.add(new Register());
		}
		Iterator <String> iter = lines.iterator(); 
		int tag = 0;
		while(iter.hasNext()) {
			String temp[] = iter.next().split(" ");
			inst.add(new Instruction(temp[0],Integer.valueOf(temp[1]),Integer.valueOf(temp[2]),Integer.valueOf(temp[3]), Integer.valueOf(temp[4]),tag));
			inst2.add(new Instruction(temp[0],Integer.valueOf(temp[1]),Integer.valueOf(temp[2]),Integer.valueOf(temp[3]), Integer.valueOf(temp[4]),tag));
			tag++;
		}
		clock = 0;
		this.m = m;
		this.N = N;
		this.D = D;
		this.k0 = k0;
		this.k1 = k1;
		this.k2 = k2;
		//change this
		this.fetch = new Fetch(D);
		this.dispQ = new DispatchQueue(D);
		this.schedQ = new SchedulingQueue(m,k0,k1,k2);
	}
	public void run() {
		ArrayList<Instruction> fetch = new ArrayList();
		ArrayList indices = new ArrayList();
		while(clock < 10) {
			
			
			
			for (int i =0; i < schedQ.size();i++) {
				
			}
			
			
			
			ArrayList toRem = new ArrayList();
			//into scheduling queue
			for(int i = 0; i < dispQ.curSize; i++) {
				
				if (dispQ.get(i).dispCycle!=clock)
					 if(schedQ.notFull(dispQ.get(i).FU)) { //check for that FU
								dispQ.get(i).schedCycle = clock;
								schedQ.add(dispQ.get(i), dispQ.get(i).FU); //add to schedQ
								if(dispQ.get(i).destReg != -1) {
									regFile.get(dispQ.get(i).destReg).ready = false;
									regFile.get(dispQ.get(i).destReg).tag = dispQ.get(i).tag;
								}
								if(dispQ.get(i).srcReg1 != -1) {
									if(regFile.get(dispQ.get(i).srcReg1).ready) {
										//set RS.srcReg1.ready = true;
										if(dispQ.get(i).FU==0) {
											schedQ.subQ0.get(schedQ.subQ0.size()-1).srcRdy1 = true;
										} else if (dispQ.get(i).FU ==1) {
											schedQ.subQ1.get(schedQ.subQ1.size()-1).srcRdy1 = true;
										} else {
											schedQ.subQ2.get(schedQ.subQ2.size()-1).srcRdy1 = true;
										}
									} else {
										//set RS.srcReg1.tag = regFile.get(dispQ.get(0).srcReg1).tag;
										//set RS.srcReg1.ready = false;
										if(dispQ.get(i).FU==0) {
											schedQ.subQ0.get(schedQ.subQ0.size()-1).srcTag1 = regFile.get(dispQ.get(i).srcReg1).tag;
											schedQ.subQ0.get(schedQ.subQ0.size()-1).srcRdy1 = false;
										} else if (dispQ.get(i).FU ==1) {
											schedQ.subQ1.get(schedQ.subQ1.size()-1).srcTag1 = regFile.get(dispQ.get(i).srcReg1).tag;
											schedQ.subQ1.get(schedQ.subQ1.size()-1).srcRdy1 = false;
										} else {
											schedQ.subQ2.get(schedQ.subQ2.size()-1).srcTag1 = regFile.get(dispQ.get(i).srcReg1).tag;
											schedQ.subQ2.get(schedQ.subQ2.size()-1).srcRdy1 = false;
										}
									}
								}
								if(dispQ.get(i).srcReg2 != -1) {
									if(regFile.get(dispQ.get(i).srcReg1).ready) {
										//set RS.srcReg1.ready = true;
										if(dispQ.get(i).FU==0) {
											schedQ.subQ0.get(schedQ.subQ0.size()-1).srcRdy1 = true;
										} else if (dispQ.get(i).FU ==1) {
											schedQ.subQ1.get(schedQ.subQ1.size()-1).srcRdy1 = true;
										} else {
											schedQ.subQ2.get(schedQ.subQ2.size()-1).srcRdy1 = true;
										}
									} else {
										//set RS.srcReg1.tag = regFile.get(dispQ.get(0).srcReg1).tag;
										//set RS.srcReg1.ready = false;
										if(dispQ.get(i).FU==0) {
											schedQ.subQ0.get(schedQ.subQ0.size()-1).srcTag1 = regFile.get(dispQ.get(i).srcReg1).tag;
											schedQ.subQ0.get(schedQ.subQ0.size()-1).srcRdy1 = false;
										} else if (dispQ.get(0).FU ==1) {
											schedQ.subQ1.get(schedQ.subQ1.size()-1).srcTag1 = regFile.get(dispQ.get(i).srcReg1).tag;
											schedQ.subQ1.get(schedQ.subQ1.size()-1).srcRdy1 = false;
										} else {
											schedQ.subQ2.get(schedQ.subQ2.size()-1).srcTag1 = regFile.get(dispQ.get(i).srcReg1).tag;
											schedQ.subQ2.get(schedQ.subQ2.size()-1).srcRdy1 = false;
										}
									}
								}
								//schedQ.add(dispQ.get(0), dispQ.get(0).FU); //add to schedQ
								inst.get(findIndex(dispQ.get(i).tag)).schedCycle = clock;
								toRem.add(i);
								//dispQ.remove(dispQ.get(i)); //remove from dispQ
							
					 } else break;
			}
			for (int i = toRem.size()-1; i>=0; i--) {
				dispQ.remove(dispQ.get((Integer) toRem.get(i))); //remove from dispQ
			}

			while(!fetch.isEmpty()) {
			//into dispatch queue
					if (fetch.get(0).fetchCycle!=clock) {
						//if dispatch queue is not full
						if(dispQ.notFull()) {
							fetch.get(0).dispCycle = clock;
							dispQ.add(fetch.get(0)); 
							inst.get((Integer) indices.get(0)).dispCycle = clock;
							fetch.remove(0);//remove from fetch
							indices.remove(0);
						}
						else //if dispatch is full, then stall
							break;
					}
			}
			
			//fetch&decode instructions to dispatch queue
			//if dispatch queue is full, stall
			//change D to N?
			for (int i =0; i < D; i++) {
				//if dispatch queue is full, stall
				if (!dispQ.notFull()) break;
				//if not full then, fetch up to N instructions
				inst.get(index).fetchCycle = clock;
				fetch.add(inst.get(index));
				indices.add(index);
				inst2.remove(0);
				index++;
			}
			
			clock++;
		
		}
		print();
		
	}
	
	private int findIndex(int tag) {
		// TODO Auto-generated method stub
		for (int i = 0; i < inst.size(); i++) {
			if (inst.get(i).tag==tag) 
				return i;
		}
		return 0;
	}
	public void print() {
		for (int i =0; i < 10; i ++) {
			System.out.println(inst.get(i).tag + " " + inst.get(i).fetchCycle + " "  + inst.get(i).dispCycle+ " "  + inst.get(i).schedCycle);
			//System.out.println(inst.get(i).tag + " " + inst.get(i).fetchCycle + " " + inst.get(i).dispCycle);
			//System.out.println(inst.get(i).addr + " " + inst.get(i).FU + " " + inst.get(i).destReg + " " + inst.get(i).srcReg1 + 
				//	  " " + inst.get(i).srcReg2);
			
		}
		 //System.out.println(inst.get(9999).tag);
	}

}

